package com.platform.modules.wallet.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.platform.common.constant.AppConstants;
import com.platform.common.enums.YesOrNoEnum;
import com.platform.common.exception.BaseException;
import com.platform.common.shiro.ShiroUtils;
import com.platform.common.utils.redis.RedisUtils;
import com.platform.common.web.domain.AjaxResult;
import com.platform.common.web.service.impl.BaseServiceImpl;
import com.platform.modules.chat.domain.*;
import com.platform.modules.chat.enums.RiskTypeEnum;
import com.platform.modules.chat.service.*;
import com.platform.modules.chat.vo.ChatVo01;
import com.platform.modules.chat.vo.ChatVo02;
import com.platform.modules.wallet.dao.WalletRedPacketDao;
import com.platform.modules.wallet.domain.*;
import com.platform.modules.wallet.enums.AccountTypeEnum;
import com.platform.modules.wallet.enums.RedPacketLuckEnum;
import com.platform.modules.wallet.enums.RedPacketStatusEnum;
import com.platform.modules.wallet.enums.RedPacketTypeEnum;
import com.platform.modules.wallet.service.*;
import com.platform.modules.wallet.utils.RandSplitNumUtils;
import com.platform.modules.wallet.utils.TransferUtils;
import com.platform.modules.wallet.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service("walletRedPacketService")
public class WalletRedPacketServiceImpl extends BaseServiceImpl<WalletRedPacket> implements WalletRedPacketService {

    @Autowired
    private RedisUtils redisUtils;
    @Resource
    private WalletRedPacketDao walletRedPacketDao;
    @Resource
    private ChatUserService chatUserService;
    @Resource
    private WalletService walletService;
    @Resource
    private WalletAccountService walletAccountService;
    @Resource
    private WalletRedPacketSubService walletRedPacketSubService;
    @Resource
    private ChatGroupService groupService;
    @Resource
    private ChatGroupInfoService groupInfoService;
    @Resource
    private TransferUtils transferUtils;
    @Resource
    private ChatGroupMonitorService groupMonitorService;
    @Resource
    private WalletConfigService configService;
    @Resource
    private ChatUserMonitorService userMonitorService;

    @Autowired
    public void setBaseDao() {
        super.setBaseDao(walletRedPacketDao);
    }

    @Override
    public WalletRedPacket transfer(ChatVo01 chatVo, Long userId) {

        JSONObject json = JSONUtil.parseObj(chatVo.getContent());
        int amount = new BigDecimal(json.getStr("amount")).multiply(BigDecimal.valueOf(100)).intValue();

        if (amount<1){
            throw new BaseException("单个红包最低限额：0.01");
        }
        if (amount > 20000) {
            throw new BaseException("单个红包限额：200");
        }

        Wallet wallet = walletService.getWallet(userId);
        // 当前用户
        ChatUser cu = chatUserService.getById(wallet.getUserId());

        RedPacketVo01 redPacketVO01 = new RedPacketVo01();
        redPacketVO01.setToId(chatVo.getUserId());
        redPacketVO01.setContent(chatVo.getContent());
        redPacketVO01.setUserId(userId);

        String lock_key = AppConstants.LOCK_PREFIX + "redpacket:single:" + wallet.getUserId();
        synchronized (this) {
            if (redisUtils.hasKey(lock_key)) {
                throw new BaseException("操作太频繁，请稍后再试");
            }
            redisUtils.set(lock_key, wallet.getUserId().toString(), AppConstants.LOCK_EXPIRE, TimeUnit.SECONDS);
        }
        transferUtils.verifySend(chatVo.getContent(), wallet, cu);
        WalletRedPacket redPacket = createRedPacket(redPacketVO01);
        redisUtils.expire(lock_key, AppConstants.LOCK_END, TimeUnit.SECONDS);

        return redPacket;
    }

    @Override
    public WalletRedPacket transfer(ChatVo02 chatVo, Long userId) {

        JSONObject json = JSONUtil.parseObj(chatVo.getContent());
        int amount = new BigDecimal(json.getStr("amount")).multiply(BigDecimal.valueOf(100)).intValue();
        Integer countAmount = json.getInt("count") * 20000;

        if (amount<1){
            throw new BaseException("单个红包最低限额：0.01");
        }
        if (amount > countAmount) {
            throw new BaseException("单个红包限额：" + countAmount);
        }

        Wallet wallet = walletService.getWallet(userId);
        // 当前用户
        ChatUser cu = chatUserService.getById(userId);

        RedPacketVo01 redPacketVO01 = new RedPacketVo01();
        redPacketVO01.setToId(chatVo.getGroupId());
        redPacketVO01.setContent(chatVo.getContent());
        redPacketVO01.setUserId(userId);
        if (ObjectUtil.isNotEmpty(chatVo.getToIds())){
            String toids = chatVo.getToIds().stream().collect(Collectors.joining(","));
            redPacketVO01.setToIds(toids);
        }

        String lock_key = AppConstants.LOCK_PREFIX + "redpacket:group:" + wallet.getUserId();
        synchronized (this) {
            if (redisUtils.hasKey(lock_key)) {
                throw new BaseException("操作太频繁，请稍后再试");
            }
            redisUtils.set(lock_key, wallet.getUserId().toString(), AppConstants.LOCK_EXPIRE, TimeUnit.SECONDS);
        }
        transferUtils.verifySend(chatVo.getContent(), wallet, cu);
        WalletRedPacket redPacket = createRedPacket(redPacketVO01);
        redisUtils.expire(lock_key, AppConstants.LOCK_END, TimeUnit.SECONDS);

        return redPacket;
    }

    @Override
    public WalletRedPacket createRedPacket(RedPacketVo01 vo) {

        JSONObject json = JSONUtil.parseObj(vo.getContent());
        int amount = new BigDecimal(json.getStr("amount")).multiply(BigDecimal.valueOf(100)).intValue();
        String queryId = json.getStr("queryId");
        RedPacketTypeEnum type = json.get("type", RedPacketTypeEnum.class);
        Integer count = json.getInt("count");
        String name = json.getStr("name");

        RedPacketVo redPacketVo = new RedPacketVo();
        redPacketVo.setFromId(vo.getUserId());
        redPacketVo.setToId(vo.getToId());
        redPacketVo.setAmount(amount);
        redPacketVo.setQueryId(queryId);
        redPacketVo.setType(type);
        redPacketVo.setCount(count);
        redPacketVo.setName(name);
        redPacketVo.setToIds(vo.getToIds());

        return create(redPacketVo);
    }

    @Override
    public RedPacketSubVo01 openRedPacket(String id, Long userId) {

        WalletRedPacket redPacket = getByQueryId(id);

        if (!RedPacketStatusEnum.SUCCESS.equals(redPacket.getStatus())) {
            throw new BaseException("红包转态异常，Status:" + redPacket.getStatus());
        }

        if (RedPacketTypeEnum.GROUP.equals(redPacket.getType())) {

            // 查询群明细
            ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(redPacket.getToId(), userId, YesOrNoEnum.NO);
            if (groupInfo == null || YesOrNoEnum.YES.equals(groupInfo.getKicked())) {
                throw new BaseException("群权限限制");
            }

            ChatGroup group;
            String groupKey = StrUtil.format(AppConstants.REDIS_GROUP, redPacket.getToId());
            if (redisUtils.hasKey(groupKey)) {
                group = JSONUtil.toBean(redisUtils.get(groupKey), ChatGroup.class);
            } else {
                group = groupService.getById(redPacket.getToId());
                redisUtils.set(groupKey, JSONUtil.toJsonStr(group), AppConstants.REDIS_GROUP_TIME, TimeUnit.MINUTES);
            }

            if (YesOrNoEnum.YES.equals(group.getWhite())) {

                if (!groupService.isMasterOrManage(group, userId)){
                    if (StrUtil.isNotBlank(group.getWhiteIds())) {
                        String[] whiteIds = group.getWhiteIds().split(",");
                        if (!Arrays.asList(whiteIds).contains(String.valueOf(userId))) {
                            throw new BaseException("您不能领取该红包");
                        }
                    }
                }
            }

            if (StrUtil.isNotBlank(redPacket.getToIds())) {
                String[] toIds = redPacket.getToIds().split(",");
                if (!Arrays.asList(toIds).contains(String.valueOf(userId))) {
                    throw new BaseException("您不能领取该红包");
                }
            }

            String key = AppConstants.WALLET_RED_PACKET + redPacket.getId();
            if (redisUtils.getExpire(key) != -2 && redisUtils.sSize(key).intValue() == 0) {
                throw new BaseException("手慢了，红包派完了");
            }
        }

        return walletRedPacketSubService.openRedPacket(redPacket.getId(), userId);
    }

    @Override
    public boolean findToIdByUserId(Long id, Long userId) {

        List<WalletRedPacketSub> walletRedPacketSubs = walletRedPacketSubService.queryByToId(id, userId);

        if (walletRedPacketSubs.size() > 0) {
            return true;
        }

        return false;
    }

    @Override
    public List<WalletRedPacketSub> queryByRedRacketId(Long id) {
        return walletRedPacketSubService.queryByRedRacketId(id);
    }

    @Override
    public List<WalletRedPacketSub> querySubSuccessList(Long id) {
        return walletRedPacketSubService.queryByStatusSuccess(id);
    }

    @Override
    public List<WalletRedPacketSub> querySubInitList(Long id) {
        return walletRedPacketSubService.queryByStatusInit(id);
    }

    @Override
    public WalletRedPacketSub getLuck(Long id) {
        List<WalletRedPacketSub> walletRedPacketSubs = walletRedPacketSubService.queryByStatusInit(id);
        for (WalletRedPacketSub walletRedPacketSub : walletRedPacketSubs) {
            if (RedPacketLuckEnum.YES.equals(walletRedPacketSub.getBestLuck())) {
                return walletRedPacketSub;
            }
        }
        return null;
    }

    @Override
    public WalletRedPacket getByQueryId(String queryId) {
        WalletRedPacket redPacket = new WalletRedPacket()
                .selectOne(
                        new LambdaQueryWrapper<WalletRedPacket>()
                                .eq(WalletRedPacket::getQueryId, queryId));
        if (redPacket == null) {
            throw new BaseException("对象不存在");
        }
        return redPacket;

    }

    @Override
    public Integer queryTodayNotUserId(Long groupId, List<Long> userIds) {

        return walletRedPacketDao.queryTodayNotUserIdByGroup(groupId, userIds, DateUtil.today());
//        return new WalletRedPacket().selectList(new LambdaQueryWrapper<WalletRedPacket>()
//                .eq(WalletRedPacket::getToId, groupId)
//                .eq(WalletRedPacket::getType, RedPacketTypeEnum.GROUP)
//                .notIn(ObjectUtil.isNotEmpty(userIds), WalletRedPacket::getFromId, userIds)
//                .eq(WalletRedPacket::getCreateDate, DateUtil.today())
//                .eq(WalletRedPacket::getStatus, RedPacketStatusEnum.SUCCESS));
    }

    @Override
    public Integer queryTodayByGroupIdAndUserId(Long groupId, Long userId) {
        return walletRedPacketDao.queryTodayByGroupIdAndUserId(groupId, userId, DateUtil.today());
    }

    @Override
    public Integer clearRecord(String beforeToday) {
        return walletRedPacketDao.clearRecord(beforeToday);
    }

    @Transactional(rollbackFor = Exception.class)
    public WalletRedPacket create(RedPacketVo vo) {

        WalletRedPacket bean = getBean();
        bean.setQueryId(vo.getQueryId());
        bean.setAmount(vo.getAmount());
        bean.setFromId(vo.getFromId());
        bean.setToId(vo.getToId());
        bean.setType(vo.getType());
        bean.setName(vo.getName());
        bean.setToIds(vo.getToIds());
        if (RedPacketTypeEnum.SINGLE.equals(vo.getType())) {
            bean.setCount(1);
        } else {
            bean.setCount(vo.getCount());
        }
        add(bean);

        List<Integer> list = getRand(bean, vo.getType());

        Comparator<Integer> comparator = Comparator.comparing(Integer::intValue);
        int max = list.stream().max(comparator).get().intValue();
        String key = AppConstants.WALLET_RED_PACKET + bean.getId();
        for (Integer integer : list) {

            int amount = integer.intValue();
            RedPacketSubVo subVo = new RedPacketSubVo();
            subVo.setRedpacketId(bean.getId());
            subVo.setAmount(amount);
            subVo.setFromId(bean.getFromId());
            if (max == integer) {
                subVo.setBestLuck(RedPacketLuckEnum.YES);
            } else {
                subVo.setBestLuck(RedPacketLuckEnum.NO);
            }

            //把所有红包ID放进待抽奖集合
            WalletRedPacketSub redPacketSub = walletRedPacketSubService.createRedPacketSub(subVo);
            redisUtils.sAdd(key, String.valueOf(redPacketSub.getId()));
        }

        redisUtils.expire(key, 30, TimeUnit.SECONDS);

        AccountVo01 accountVo = new AccountVo01();
        accountVo.setOtherId(bean.getId());
        accountVo.setAmount(vo.getAmount());
        accountVo.setType(AccountTypeEnum.RED_PACKET_OUT);
        accountVo.setUserId(vo.getFromId());

        WalletAccount account = walletAccountService.createRecord(accountVo, WalletAccountService.NEGATIVE);

        walletAccountService.successRecord(account.getId());//完成账本记录
//        walletService.subtract(accountVo.getAmount(), account.getUserId());//减掉余额
        successOut(bean.getId());//完成出账（出账）
        return bean;
    }

    public static boolean verify(String name, List<Integer> voList) {

        if (NumberUtil.isNumber(name)) {//是否是纯数字

            String[] arr = new String[voList.size()];

            for (int i = 0; i < voList.size(); i++) {
                String amoutnStr = String.valueOf(voList.get(i));
                arr[i] = StrUtil.sub(amoutnStr, amoutnStr.length() - 1, amoutnStr.length());
            }

            char[] chars = name.toCharArray();
            for (char a : chars) {
                if (!ArrayUtil.contains(arr, String.valueOf(a))) {//只要有一个对不上就推出了
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 完成转出状态
     *
     * @param id
     */
    private void successOut(Long id) {
        WalletRedPacket walletRedPacket = findById(id);
        walletRedPacket.setStatus(RedPacketStatusEnum.SUCCESS);
        walletRedPacket.setSuccessTime(DateUtil.date());
        updateById(walletRedPacket);
    }

    private List<Integer> getRand(WalletRedPacket bean, RedPacketTypeEnum type) {

        List<Integer> list = RandSplitNumUtils.genRandList(bean.getAmount(), bean.getCount(), 1, bean.getAmount());

        for (int i = 0; i < 100; i++) {
            if (isDistinct(list)) break;
            list = RandSplitNumUtils.genRandList(bean.getAmount(), bean.getCount(), 1, bean.getAmount());
        }

        if (RedPacketTypeEnum.GROUP.equals(type)) {

            ChatGroupMonitor groupMonitor = groupMonitorService.getByGroupId(bean.getToId());

            if (ObjectUtil.isNotEmpty(groupMonitor)) {

                if (StrUtil.isNotBlank(groupMonitor.getNotUserId())) {
                    String[] split = groupMonitor.getNotUserId().split(",");
                    if (ArrayUtil.contains(split, String.valueOf(bean.getFromId()))) {
                        return list;
                    }
                }

                WalletConfig group = configService.getByKey("group");
                if (RiskTypeEnum.YES.equals(groupMonitor.getRisk())) {
                    for (int i = 0; i < Integer.parseInt(group.getValue()); i++) {
                        if (verify(bean.getName(), list) && isDistinct(list)) {
                            break;
                        }
                        list = RandSplitNumUtils.genRandList(bean.getAmount(), bean.getCount(), 1, bean.getAmount());
                    }
                } else if (RiskTypeEnum.NO.equals(groupMonitor.getRisk())) {
                    for (int i = 0; i < Integer.parseInt(group.getValue()); i++) {
                        if (!verify(bean.getName(), list) && isDistinct(list)) {
                            break;
                        }
                        list = RandSplitNumUtils.genRandList(bean.getAmount(), bean.getCount(), 1, bean.getAmount());
                    }
                }
            }

            Wallet wallet = walletService.getWallet(bean.getFromId());
            if (YesOrNoEnum.NO.equals(wallet.getOpenRisk())) {
                return list;
            }
            ChatUserMonitor userMonitor = userMonitorService.getTotal(bean.getToId(), bean.getFromId());
//            WalletConfig single = configService.getByKey("single");

            if (RiskTypeEnum.YES.equals(userMonitor.getRisk())) {
                for (int i = 0; i < wallet.getRisk(); i++) {
                    if (verify(bean.getName(), list) && isDistinct(list)) {
                        break;
                    }
                    list = RandSplitNumUtils.genRandList(bean.getAmount(), bean.getCount(), 1, bean.getAmount());
                }
            } else if (RiskTypeEnum.NO.equals(userMonitor.getRisk())) {
                for (int i = 0; i < wallet.getRisk(); i++) {
                    if (!verify(bean.getName(), list) && isDistinct(list)) {
                        break;
                    }
                    list = RandSplitNumUtils.genRandList(bean.getAmount(), bean.getCount(), 1, bean.getAmount());
                }
            }
        }

        return list;
    }

    private boolean isDistinct(List<Integer> list) {
        long count = list.stream().distinct().count();
        return list.size() == count;
    }

    private WalletRedPacket getBean() {
        return new WalletRedPacket()
                .setCreateDate(DateUtil.date())
                .setCreateTime(DateUtil.date())
                .setStatus(RedPacketStatusEnum.INIT);
    }
}
